/**
 * JWT认证过滤器 - 处理JWT令牌的验证和认证上下文设置
 *
 * 开发人员：徐少洋
 * 开发时间：2025-10-20 至 2025-10-25
 * 模块职责：拦截HTTP请求，验证JWT令牌的有效性，建立用户认证上下文
 *
 * 技术要点：
 * - 继承OncePerRequestFilter确保每次请求只执行一次
 * - JWT令牌解析和验证机制
 * - Spring Security认证上下文设置
 * - 无效令牌的优雅处理（允许匿名访问公开端点）
 * - 多租户用户信息提取和传递
 *
 * 开发故事：负责JWT认证过滤器的设计和实现，与安全配置和认证服务深度配合，确保系统的认证安全性和用户会话管理。
 */
package com.pbl.security;

import io.jsonwebtoken.Claims;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;

@Component
public class JwtAuthenticationFilter extends org.springframework.web.filter.OncePerRequestFilter {

    private final JwtUtil jwtUtil;

    /**
     * 构造JWT认证过滤器
     * 开发环境使用固定的密钥，生产环境应通过配置注入
     */
    public JwtAuthenticationFilter() {
        // 开发环境fallback密钥，生产环境应通过配置注入获得
        this.jwtUtil = new JwtUtil("pbl-secret-key-please-change-please-change-123456", 24 * 3600_000L);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String header = request.getHeader("Authorization");
        if (StringUtils.hasText(header) && header.startsWith("Bearer ")) {
            String token = header.substring(7);
            try {
                Claims claims = jwtUtil.parseClaims(token);
                String subject = claims.getSubject();
                UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
                        new User(subject, "", Collections.emptyList()), null, Collections.emptyList());
                auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(auth);
            } catch (Exception ignored) {
                // ignore invalid token to allow anonymous for permitted endpoints
            }
        }
        filterChain.doFilter(request, response);
    }
}
